#pragma once

#include<iostream>
#include<memory>
#include<string>
#include<functional>
#include<unordered_map>
#include"Socket.hpp"
#include"Epoller.hpp"
#include"Log.hpp"
#include"nocopy.hpp"
#include"common.hpp"

uint32_t EVENT_IN = (EPOLLIN | EPOLLET);
uint32_t EVENT_OUT = (EPOLLOUT | EPOLLET);
const int g_buffer_size = 1024;

class Connection;
class TcpServer;

using func_t = std::function<void(std::shared_ptr<Connection>)>; 
using except_func = std::function<void(std::weak_ptr<Connection>)>;

class Connection 
{
public: 
    Connection(int sock,std::shared_ptr<TcpServer> tcp_server_ptr)
    :_sock(sock),
     _tcp_server_ptr(tcp_server_ptr)
    {}

    void SetHandler(func_t recv_cb,func_t send_cb,except_func except_cb)
    {
        _recv_cb = recv_cb;
        _send_cb = send_cb;
        _except_cb = except_cb;
    }

    int SockFd()
    {
        return _sock;
    } 

    void AppendInBuffer(const std::string& info)
    {
        _inbuffer += info;
    }

    void AppendOutBuffer(const std::string& info)
    {
        _outbuffer += info;
    }

    std::string& Inbuffer()
    {
        return _inbuffer;
    }

    std::string& OutBuffer()
    {
        return _outbuffer;
    }

    ~Connection()
    {}

private:
    int _sock;
    std::string _inbuffer;
    std::string _outbuffer;
public:
    func_t _recv_cb;
    func_t _send_cb;
    func_t _except_cb;

    //回指指针
    std::shared_ptr<TcpServer> _tcp_server_ptr; 

    std::string _ip;
    uint16_t _port;

};


class TcpServer:public nocopy
{
    static const int num = 64;
public:
    TcpServer(uint16_t port,func_t OnMessage)
    :_port(port),
     _OnMessage(OnMessage),
     _epoller_ptr(new Epoller()),
     _listensock_ptr(new Sock()),
     _quit(true)
    {

    }
    void Init()
    {
        _listensock_ptr->Socket();
        SetNonBlockOrDie(_listensock_ptr->Fd());
        _listensock_ptr->Bind(_port);
        _listensock_ptr->Listen();
        lg(Info, "create listen socket success: %d\n", _listensock_ptr->Fd());
        AddConnection(_listensock_ptr->Fd(),EVENT_IN,\
        std::bind(&TcpServer::Acceptor,this,std::placeholders::_1),nullptr,nullptr);
    }

    void Dispatcher(int timeout)
    {
        int n = _epoller_ptr->EpollerWait(revs,num,timeout);
        for(int i = 0; i < n ;i++)
        {
            uint32_t events = revs[i].events;
            int sock = revs[i].data.fd;
            //统一把事件异常转化为读写问题
            if(events & EPOLLERR)
            {
                events |= (EPOLLIN |EPOLLOUT);
            }
            if(events & EPOLLHUP)
            {
                events |= (EPOLLIN |EPOLLOUT);
            }
            //只需要处理EPOLLIN,EPOLLOUT
            if((events & EPOLLIN) && IsConnectionSafe(sock))
            {
                if(_connections[sock]->_recv_cb)
                {
                    _connections[sock]->_recv_cb(_connections[sock]);
                }
            }
            if((events & EPOLLOUT) && IsConnectionSafe(sock))
            {
                if(_connections[sock]->_send_cb)
                {
                    _connections[sock]->_send_cb(_connections[sock]);
                }
            }
        }
    }

    void AddConnection(int sock,uint32_t event,func_t _recv_cb,func_t _send_cb,except_func _except_cb,\
        const std::string& ip = "0.0.0.0",uint16_t port = 0)
    {
        //1.给所有sock也建立一个Connection对象,将_listensock添加到Connenction中,同时_listensock和Connection
        //放入_connections
        //_epoller_ptr->EpollerUpdate(EPOLL_CTL_ADD,_listensock_ptr->Fd(),EVENT_IN);
        std::shared_ptr<Connection> new_connection = std::make_shared<Connection> (sock,std::shared_ptr<TcpServer>(this));
        new_connection->SetHandler(_recv_cb,_send_cb,_except_cb);
        new_connection->_ip = ip;
        new_connection->_port = port;
        //2.添加到unorder_map中
        _connections.insert(std::make_pair(sock,new_connection));
        //3.添加对应的事件,不仅要添加到内核中,
        _epoller_ptr->EpollerUpdate(EPOLL_CTL_ADD,sock,event);
        lg(Debug,"add a new connection success ,sockfd is %d",sock);
    }

    bool IsConnectionSafe(int fd)
    {
        auto iter = _connections.find(fd);
        if(iter == _connections.end())
        {
            return false;
        }
        else
        {
            return true;
        }
    }

    //连接管理器

    void Acceptor(std::shared_ptr<Connection> connection)
    {
        while(true)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int sock = accept(connection->SockFd(),(struct sockaddr*)&peer,&len); 
            if(sock > 0)
            {
                uint16_t peerport = ntohs(peer.sin_port);
                char ipbuf[128];
                inet_ntop(AF_INET,&peer.sin_addr.s_addr,ipbuf,sizeof(ipbuf));
                std::string peerip = ipbuf;
                lg(Debug,"get a new client,get info-> [%s:%d] , sockfd: %d",peerip,peerport,sock);

                SetNonBlockOrDie(sock);
                //listensock套接字只需要考虑recv_cb,而其他事件需要考虑读,写
                AddConnection(sock,EVENT_IN,\
                    std::bind(&TcpServer::Recver,this,std::placeholders::_1),\
                    std::bind(&TcpServer::Sender,this,std::placeholders::_1),\
                    std::bind(&TcpServer::Excepter,this,std::placeholders::_1),\
                    peerip,peerport);
            }
            else
            {
                if(errno == EWOULDBLOCK)
                {
                    break;
                }
                else if(errno == EINTR)
                {
                    continue;
                }
                else
                {
                    break;
                }
            }
        }
    }

    //事件管理器
    void Recver(std::shared_ptr<Connection> connection)
    {
        while(true)
        {
            int sock = connection->SockFd();
            char buffer[g_buffer_size];
            memset(buffer,0,sizeof(buffer));
            ssize_t n = recv(sock,buffer,sizeof(buffer) - 1 , 0);//之前设置过非阻塞读取
            if(n > 0)
            {
                connection->AppendInBuffer(buffer);
            }
            else if(n == 0)
            {
                lg(Info,"sockfd: %d,client info %s:%d quit...",sock,connection->_ip.c_str(),connection->_port);
                connection->_except_cb(connection);
            }
            else
            {
                if(errno == EWOULDBLOCK)
                {
                    break;
                }
                else if(errno == EINTR)
                {
                    continue;
                }
                else
                {
                    lg(Warning,"sockfd: %d,client info %s:%d recv error...",sock,connection->_ip.c_str(),connection->_port);
                    connection->_except_cb(connection);
                    return;
                }
            }
            //数据有了,但不一定全,1.检测2.处理完整报文
            _OnMessage(connection);
        }
    }
    void Sender(std::shared_ptr<Connection> connection)
    {
        auto & outbuffer = connection->OutBuffer();
        while(true)
        {
            ssize_t n = send(connection->SockFd(),outbuffer.c_str(),outbuffer.size(),0);
            if(n > 0)
            {
                outbuffer.erase(0,n);
                if(outbuffer.empty())
                {
                    break;
                }
            }
            else if(n == 0)
            {
                return;
            }
            else
            {
                if(errno == EWOULDBLOCK)
                {
                    break;
                }
                else if(errno == EINTR)
                {
                    continue;
                }
                else
                {
                    lg(Warning,"sockfd: %d,client info %s:%d send error...",connection->SockFd(),connection->_ip.c_str(),connection->_port);
                    connection->_except_cb(connection);
                    return;
                }
            }
        }
        if(!outbuffer.empty())
        {
            //开启对写事件的关心
            EnableEvent(connection->SockFd(),true,true);
        }
        else
        {
            //关闭对写事件的关心
            EnableEvent(connection->SockFd(),true,false);
        }

        
    }
    void Excepter(std::weak_ptr<Connection> connection)
    {
        if(connection.expired())
        {
            return;
        }
        auto conn = connection.lock();
        int fd = conn->SockFd();
        lg(Info,"Except Hander sockfd: %d,client info %s:%d recv error...",conn,conn->_ip.c_str(),conn->_port);
        _epoller_ptr->EpollerUpdate(EPOLL_CTL_DEL,fd,0);
        //2.关闭异常的文件描述符
        close(fd);
        lg(Debug,"close %d done...\n",fd);
        //3.从unordered_map中移除
        //_connections[fd].reset();
        //_connections.erase(fd);
        lg(Debug,"remove %d from _connections...\n",fd);
    }

    void EnableEvent(int sock,bool readable,bool writeable)
    {
        uint32_t events = 0;
        events |= ((readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0) | EPOLLET);
        _epoller_ptr->EpollerUpdate(EPOLL_CTL_MOD,sock,events);
    }

    void Loop()
    {
        _quit = false;
        
        //AddConnection();
        while(!_quit)
        {
            Dispatcher(3000);
            PrintConnection();
        } 
        _quit = true;
    }

    void PrintConnection()
    {
        std::cout<<"_connections fd list"<<std::endl;
        for(auto& connection: _connections)
        {
            std::cout<< connection.second->SockFd()<<",";
            std::cout<<"inbuffer:"<<connection.second->Inbuffer();
        }
        std::cout<<std::endl;
    }

    ~TcpServer()
    {}
private:
    std::shared_ptr<Epoller> _epoller_ptr;
    std::shared_ptr<Sock> _listensock_ptr;
    std::unordered_map<int,std::shared_ptr<Connection>> _connections;   //内核
    struct epoll_event revs[num]; 
    uint16_t _port;
    bool _quit;

    //处理上层数据
    func_t _OnMessage;
};